/**
 * @Created Aug 11, 2010
 */
package com.philip.journal.common;

import org.apache.log4j.Logger;

import com.philip.journal.core.Messages.Error;

/**
 * @author cry30
 */
public final class StringUtils {

    /** Singleton class variable reference. */
    private static final StringUtils INSTANCE = new StringUtils();

    /** Minimum text String size. */
    static final int MIN_STR_LENGTH = 3;

    /** Prevent instantiation of this class. */
    private StringUtils() {}

    /**
     * Factory method.
     *
     * @return Singleton instance of this class.
     */
    public static StringUtils getInstance() {
        return INSTANCE;
    }

    /** Instance logger. */
    private final Logger logger = Logger.getLogger(getClass()); //NOPMD

    /**
     * Limits a String and adds an ellipsis if the limit is exceeded. Smallest limit is 3.
     *
     * @param string test String to limit.
     * @param limit maximum number of characters allowed.
     * @return the
     * @exception IllegalArgumentException when the limit is less than 3.
     */
    public String limitString(final String string, final int limit) {
        if (limit < MIN_STR_LENGTH) {
            throw new IllegalArgumentException("Limit parameter cannot be less than 3.");
        }

        String retval = null;
        if (string != null) {
            if (string.length() > limit) {
                retval = string.substring(0, limit - MIN_STR_LENGTH) + "...";
            } else {
                retval = string;
            }
        }
        return retval;
    }

    /**
     * Will remove the tags from the input string. <BR>
     * will be replaced with a new line. &nbsp; will be replaced by spaces.
     *
     * @param html - the html string to be stripped.
     * @return text String with html tags removed.
     * @exception IllegalArgumentException when the html parameter passed is null.
     */
    public String removeHtmlTags(final String html) {
        if (html == null) {
            throw new IllegalArgumentException(Error.IAE_NULL);
        }
        String retval = html;
        retval = retval.replaceAll("(?i)<br>", "\n");
        retval = retval.replaceAll("(?i)&gt;", ">");
        retval = retval.replaceAll("(?i)&lt;", "<");
        retval = retval.replaceAll("(?i)&nbsp;", " ");
        retval = retval.replaceAll("(<[^>]+>)", "");

        return retval;
    }

    /**
     * Safely returns the substring even if you specify longer endIdx.
     *
     * @param string the string.
     * @param startIdx start index.
     * @param endIdx end index.
     * @return the substring.
     *
     */
    public String substring(final String string, final int startIdx, final int endIdx) {
        //getLogger().debug("startIdx: " + startIdx + ", endIdx: " + endIdx);
        String retval = null;
        if (startIdx == -1) {
            retval = string;
        } else if (endIdx <= string.length()) {
            retval = string.substring(startIdx, endIdx);
        } else {
            retval = string.substring(startIdx);
        }
        return retval;
    }

    /**
     * Converts the stack trace to a string object.
     *
     * @param exception - the throwable instance of which to translate.
     * @return String representation of the stack trace.
     * @exception IllegalArgumentException when the e parameter is null.
     */
    public String stackTraceToString(final Throwable exception) {
        if (exception == null) {
            throw new IllegalArgumentException(Error.IAE_NULL);
        }
        final StackTraceElement[] steArr = exception.getStackTrace();
        final StringBuilder strBuilder = new StringBuilder();
        for (final StackTraceElement stackTraceElement : steArr) {
            strBuilder.append(stackTraceElement.toString());
            strBuilder.append("\n");
        }
        return strBuilder.toString();
    }

    /**
     * RTFC. Too simple to test.
     *
     * @param string String to check.
     * @return rtfc.
     */
    public boolean isNullOrEmpty(final String string) {
        return string == null || "".equals(string);
    }

    /**
     * TODO: Under construction.
     *
     * Works like {@link java.lang.String#indexOf(String, int)} except it will search backwards from end to
     * start.
     *
     * The source is the character array being searched, and the target is the string being searched for.
     *
     * @param string the string to search.
     * @param str the substring for which to search.
     * @param pFromIndex the index from which to start the search.
     * @return the index within this string of the first occurrence of the specified substring, starting at
     *         the specified index, from right to left.
     */
    public int indexOfReverse(final String string, final String str, final int pFromIndex)
    {
        final int sourceCount = string.length();
        final int targetCount = str.length();
        final char[] target = str.toCharArray();
        final char[] source = string.toCharArray();
        final int sourceOffset = 0;
        final int targetOffset = 0;
        int fromIndex = pFromIndex;

        if (fromIndex <= sourceOffset) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (pFromIndex > sourceCount) {
            fromIndex = sourceCount;
        }
        if (targetCount == 0) {
            return pFromIndex;
        }

        final char first = target[targetOffset];
        final int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + pFromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first) {
                    i += 0;
                }
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                final int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++) {
                    k += 0;
                }

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }

    /**
     * @return the logger
     */
    public Logger getLogger() {
        return logger;
    }
}
